\chapter{ORMs and Gantry}
\label{chap:orms}

One of the biggest trends of the last five years, or so, is the development
of Object-Relational Mappers (ORMs), which are fa{\c c}ades between generally
object-oriented applications and
the generally non-object relational databases which back them.  ORMs are
nice, but there are too many to choose from when you get into the market.
Gantry has its own ORM, though even we don't use it much.  It further
supports DBIx::Class and Class::DBI\footnote{DBIx::Class is preferred, since
Class::DBI is a poor team player.  When it comes to sharing the database handle
with non-Class::DBI modules, all bets are off.}.

But, chances are reasonably good that you either already use a different
ORM or are wishing you could.  This chapter explains how to fully integrate
your ORM into Gantry.  Once you do that, you can use it as easily as the
supported ORMs.

\section{Plugin Schemes}

Our current preferred connection management scheme involves writing a
plugin\index{plugin!ORM}
for the ORM.  This is what we use in DBIx::Class.
To determine whether you need a plugin or a more traditional approach reduces
to this question: Does your ORM expect the models to handle connections?
If the answer is yes, you need the old approach of the next section.
If the models don't handle connection, you should make a plugin, so
the controllers can handle it.

Connection plugins are simple, mostly because the controllers drive them.
For instance, here is the operative code in the plugin for
DBIx::Class:\index{DBIx::Class!ORM plugin}\index{ORM!plugin!DBIx::Class}

{\footnotesize
\begin{verbatim}
package Gantry::Plugins::DBIxClassConn;
use strict; use warnings;

use base 'Exporter';

our @EXPORT = qw( get_schema get_auth_schema );

sub get_schema {
    my $self = shift;

    return $self->{__SCHEMA__} if defined $self->{__SCHEMA__};

    my $base = $self->schema_base_class;

    $self->{__SCHEMA__} = $base->connect(
        $self->fish_config( 'dbconn' ),
        $self->fish_config( 'dbuser' ),
        $self->fish_config( 'dbpass' ),
        $base->get_db_options
    );

    return $self->{__SCHEMA__};
}
\end{verbatim}
}

This module exports a new method into the site object called
\verb+get_schema+.\index{get schema@\verb+get_schema+}
It calls
\verb+schema_base_class+\index{schema base class@\verb+schema_base_class+}
which the controller
-- or one of its parents -- must implement to make the scheme work.  That
method returns then name of the DBIx::Schema descendant for the app, the
one responsible for loading all the models.

Once the schema base class is known, it is a simple matter to call its
connect method\index{connection!method in schema}
with conf information taken from the site object by
calling its
\verb+fish_config+.\index{fish config@\verb+fish_config+}\index{config
info!via fish config@via \verb+fish_config+}
By using \verb+fish_config+, we allow
the engine to pull the config information for us in the best way.
That might be from Gantry::Conf, from elements of the config hash in
a CGI object, or from \texttt{PerlSetVar}s.  Using \verb+fish_config+ decouples
our controller from how conf information is provided.

You might have noticed that the above plugin exports a second method.
The \verb+get_auth_schema+\index{get auth schema@\verb+get_auth_schema+}
method is similar, and is used to get a second
connection to an authentication database.

Simple use of this plugin requires each method in the controller which
wants to use the databse to include code like this:

{\footnotesize
\begin{verbatim}
sub do_something {
    #...
    my $schema = $self->get_schema();
    my $row    = $schema->resultset( 'mytable' )->find( $row_id );
\end{verbatim}
}

\noindent
That becomes tedious, so Gantry::Utils::DBIxClass is provided as a model
base class with sugar
methods.  For example, the above can be written
as:

{\footnotesize
\begin{verbatim}
use App::Model::mytable qw( $MYTABLE );

sub do_something {
    #...
    my $row = $MYTABLE->gfind( $self, $row_id );
\end{verbatim}
}

Table \ref{tab:ORMsugar} lists the sugar
methods\index{sugar}\index{ORM!sugar} in Gantry::Utils::DBIxClass.
The methods which start with \verb+get+ are special.  They are designed for
use by bigtop generated methods.  To use \verb+get_listing+ yourself,
call it as a class method on a table model.  You must pass it a hash
reference of parameters.  There is one required key: \verb+schema+,
which must be the DBIx::Class::Schema object for your models.
If you only provide that key, the method will return all
of the rows in the table in \verb+foreign_display+ order.  To order the
rows, or receive pages of them, pass an additional keys in the hash reference
as shown in Table \ref{tab:getlistingparams}.

\begin{table}
\begin{center}
\begin{tabular}{l|l}
Method & Use \\
\hline
\multicolumn{2}{l}{For in Gantry web apps} \\
\hline
\verb+get_listing+ & Returns rows in foreign display order \\
\verb+get_form_selections+ & Returns a hash (ref) keyed by foreign   \\
                           & table name storing pull down            \\
                           & selections for \texttt{form.tt}         \\
\verb+gcreate+           & Makes one new row                         \\
\verb+gsearch+           & Returns rows                              \\
\verb+gfind+             & Returns one row                           \\
\verb+gfind_or_create+   & Returns a row which it might              \\
                         & have to make                              \\
\verb+gupdate_or_create+ & Updates a row which it might              \\
                         & have to make                              \\

\hline
\multicolumn{2}{l}{For scripts or non-Gantry web apps} \\
\hline

\verb+screate+           & Makes one new row                         \\
\verb+ssearch+           & Returns rows                              \\
\verb+sfind+             & Returns one row                           \\
\verb+sfind_or_create+   & Returns a row which it might              \\
                         & have to make                              \\
\verb+supdate_or_create+ & Updates a row which it might              \\
                         & have to make                              \\

\end{tabular}
\end{center}
\caption{All of Gantry::Utils::DBIxClass ORM sugar methods.}
\label{tab:ORMsugar}
\end{table}

\begin{table}
\begin{center}
\begin{tabular}{l|l}
Key & Description \\
\hline
\verb+schema+   & Required.  The DBIx::Class::Schema for                  \\
                & your models                                             \\
\verb+order_by+ & Exact SQL ORDER BY clause                               \\
\verb+rows+     & The number of rows per page                             \\
\verb+page+     & The current page number, from one, requires \verb+rows+ \\
\end{tabular}
\end{center}
\caption{Keys for get listing's optional hash parameter.}
\label{tab:getlistingparams}
\end{table}

The other get method is even more specialized.  CRUD and AutoCRUD forms
call \verb+get_form_selections+ to build lists of pull-down menu options
for foreign keys.  These have the foreign id and foreign display in them.
The display is shown to the user, while the id is used internally.
Call \verb+get_form_selections+ as a class method, passing it a hash
reference of parameters with a single key: \verb+schema+, which is
just as for \verb+get_listing+.

The other methods are highly similar in purpose to the methods of
similar names in DBIx::Class::ResultSet.  They come in two varieties, all
of them are class methods.  The ones beginning with \verb+g+ expect
a Gantry site object as their first parameter.  The ones beginning
with \verb+s+ expect a DBIx::Class::Schema object as their first parameter.
The remaining parameters are passed directly to the analogous
methods in DBIx::Class::ResultSet.  You get whatever that result
set returns.

\section{Gantry's Connection Scheme}

The first step in using a database is making a
connection\index{connection!database CDBI style}\index{database!connections}
to it.  Each
engine in Gantry employs a helper to ease that process.  The helpers
live in the
Gantry::Utils::DBConnHelper\index{Gantry::Utils::DBConnHelper}
namespace.  They 
fish database connection information from the configuration
information and deliver it to
Gantry::Utils::ModelHelper.\index{Gantry::Utils::ModelHelper}

Gantry's model helper exports \verb+db_Main+\index{db Main@\verb+db_Main+}.
Gantry::Utils::CDBI imports it.
Our Class::DBI\index{Class::DBI!connection}
models (when we had them) all inherited
from this base class, and so inherited that method.  If your ORM uses
\verb+db_Main+ in the same way Class::DBI does, you can do the same.

If your models must know their own connection information, you can probably
figure out how to easily leverage \verb+db_Main+ from
Gantry::Utils::ModelHelper into a robust
mechanism for connecting to your database with normal configuration data.

For those who are curious, Figure \ref{fig:dbconnseq} shows -- in outline
form -- how data flows from config info to become a connected database
handle.  All that remains is to make this \verb+db_Main+ available to
your models, as discussed above.

\begin{figure}
\begin{center}
{\footnotesize
\begin{verbatim}
Gantry::Utils::ModelHelper->db_Main
    Gantry::Utils::DBConnHelper::MP13->get_dbh
    [ unless dbh ]
        Gantry::Utils::DBConnHelper::MP13->get_conn_info
            DBI->connect_cached
        Gantry::Utils::DBConnHelper::MP13->set_dbh
\end{verbatim}
}
\end{center}
\caption{The call stack which draws connection info from config params and
uses it to connect to a database.}
\label{fig:dbconnseq}
\end{figure}

Caching\index{cache!database connections} of database handles, under this
old scheme,
is done at two or three levels.  The lowest
level caching is shown in Figure \ref{fig:dbconnseq}, which limits conf
parameter lookups to once per page hit.  At the second level, \verb+DBI+
is asked to \verb+connect_cached+.  Finally, you could add a module like
\verb+Apache::DBI+, which globally limits connections.

With DBIx::Class, we rely on its internal caching, but still often
employ \verb+Apache::DBI+, since not all of our apps are shiny new Gantry ones.

\section{ORMs and AutoCRUD}

Once you can connect to the database, it is time to turn loose the power
of Gantry and your ORM.  The chief place where they come together is
in AutoCRUD.  There are two steps to making your ORM work with
AutoCRUD:\index{ORM!and AutoCRUD}\index{AutoCRUD!and ORMs}
implementing a helper and letting AutoCRUD know about it.
Letting AutoCRUD know about your helper is easy.  Simply implement this
sub in your base controller:\index{get orm helper@\verb+get_orm_helper+}

{\footnotesize
\begin{verbatim}
sub get_orm_helper { return 'YourHelper'; }
\end{verbatim}
}

Gantry::Plugins::AutoCRUDHelper:: is the
traditional helper namespace, but you can put yours anywhere.
Implementing an AutoCRUD helper is not hard.  The module needs
methods, which are described below.  All of the methods are invoked
as class methods.

\subsection{insert}

The first method is used by \verb+do_add+ and is called \verb+insert+.
It receives the Gantry site object and the hash of values for the new
row.  It must return the new row.  If your ORM does not populate the
id of the row, you should do that manually.

\subsection{retrieve}

Next is \verb+retrieve+.\index{retrieve!AutoCRUD helper method}
It too is called with the Gantry site object, but
it receives the id of the row you should retrieve.  Return the row with that
id.

\subsection{update}

Next is \verb+update+.\index{update!AutoCRUD helper method}
It is called with the Gantry site object, the row AutoCRUD
got from calling your retrieve method, and the hash of values that should
go directly into the row.  Any return value is ignored.

\subsection{delete}

Finally, \verb+delete+\index{delete!AutoCRUD helper method}
is called with the Gantry site object, and the doomed row.
Any return value is ignored. \\

Keep in mind that AutoCRUD is all about auto and that includes autocommit.
You need to ensure that your helper routines commit to the database.

\section*{Summary}

This chapter concludes our detailed look at Gantry by showing how
to integrate your favorite ORM into it.  The rest of the book
covers Bigtop in detail.
